#define XMD_H

#include <boost/gil/typedefs.hpp>
#include <boost/gil/color_convert.hpp>
#include <boost/gil/extension/io/jpeg_io.hpp>
#include <boost/gil/extension/io/png_io.hpp>
#include <boost/gil/extension/io/tiff_io.hpp>

#include "../ged/typedefs.hpp"
#include "../ged/Command.hpp"
#include "../ged/Editor.hpp"
#include "../ged/Observer.hpp"

#include "CommandWindows.h"
#include "ImageEditorMainWindow.h"

using namespace ged;
using namespace boost::gil;

ImageEditorMainWindow::ImageEditorMainWindow(wxWindow* parent) : MainWindow( parent ), panel_width_(0), panel_height_(0), x_middle_(0), y_middle_(0), x_offset_(0), y_offset_(0)
{
	main_panel_->SetBackgroundColour(*wxWHITE);
	main_panel_->SetForegroundColour(*wxWHITE);

	Editor *editor = Editor::getInstance();
	editor->setObserver(this);
}

void ImageEditorMainWindow::doCommand(PCommand cmd) {
	try {
		Editor *editor = Editor::getInstance();
		editor->execute(cmd);
	}
	catch(Editor::NoImageException &e) {
		wxMessageDialog msg(main_panel_, _("No image to execute operation"));
		msg.ShowModal();
	}
}

void ImageEditorMainWindow::open(wxCommandEvent& event) {
	wxString wildCard = _("JPEG files (*.jpg)|*.jpg|PNG files (*.png)|*.png|TIFF files (*.tiff)|*.tiff");
	wxFileDialog fileOpen(main_panel_, _("Open File"), _(""), _(""), wildCard, wxFD_OPEN);

	int status = fileOpen.ShowModal();
	if(status != wxID_OK)
		return;

	filetype_ = fileOpen.GetFilterIndex();
	wxFileName tmp(fileOpen.GetPath());
	path_ = tmp.GetFullPath().mb_str();

	PGILImage image_tmp(new GILImage);
	switch (filetype_) {
		case 0: // JPEG file
			jpeg_read_image(path_, *image_tmp);
			break;
		case 1: // PNG file
			png_read_image(path_, *image_tmp);
			break;
		case 2: // TIFF file
			tiff_read_image(path_, *image_tmp);
			break;
		default:
			break;
	}

	Editor *PEditor = Editor::getInstance();
	PEditor->addImageLayer(image_tmp);
}

void ImageEditorMainWindow::save(wxCommandEvent& event) {
	Editor *PEditor = Editor::getInstance();

	Image::view_type image_view = PEditor->getImage()->getView();
 	gray8_image_t gray_image(image_view.width(), image_view.height());
 	gray8_view_t gray_image_view = view(gray_image);

	switch (filetype_) {
		case 0:
			jpeg_write_view(path_, PEditor->getImage()->getView());
			break;
		case 1:
			png_write_view(path_, PEditor->getImage()->getView());
			break;
		case 2:
			tiff_write_view(path_, PEditor->getImage()->getView());
			break;
		case 3:
 			copy_pixels(color_converted_view<gray8_pixel_t>(image_view), gray_image_view);
 			jpeg_write_view(path_, gray_image_view);
			break;
		default:
			break;
	}
}

void ImageEditorMainWindow::save_as(wxCommandEvent& event) {
	wxString wildcard = _("JPEG image (*.jpg)|*.jpg|PNG image (*.png)|*.png|TIFF image (*.tiff)|*.tiff|GRAY 8-bit image (*.jpg)|*.jpg");
	wxFileDialog fileSave(main_panel_, _("Save File"), _(""), _(""), wildcard, wxFD_SAVE|wxFD_OVERWRITE_PROMPT|wxFD_CHANGE_DIR);

	int status = fileSave.ShowModal();
	if(status != wxID_OK)
		return;

	filetype_ = fileSave.GetFilterIndex();
	wxFileName tmp(fileSave.GetPath());
	path_ = tmp.GetFullPath().mb_str();

	save(event);
}

void ImageEditorMainWindow::above_layer( wxCommandEvent& event ) {
	PCommand above(new SelectLayerAboveCommand);
	doCommand(above);
}

void ImageEditorMainWindow::below_layer( wxCommandEvent& event ) {
	PCommand below(new SelectLayerBelowCommand);
	doCommand(below);
}

void ImageEditorMainWindow::move( wxCommandEvent& event ) {
	Editor *editor = Editor::getInstance();
	GILPoint point;
	int max_offset_w, max_offset_h;
	try {
		point = editor->getImage()->getLayer()->getOffset();
		max_offset_w = editor->getImage()->getView().width();
		max_offset_h = editor->getImage()->getView().height();
		ImageEditorMoveWindow move_window(main_panel_, point.x, point.y, max_offset_w, max_offset_h);
		if(move_window.ShowModal() == wxID_OK) {
			x_offset_ = move_window.get_x_offset();
			y_offset_ = move_window.get_y_offset();

			PCommand move(new MoveLayerCommand(x_offset_, y_offset_));
			doCommand(move);
		}
	}
	catch(Editor::NoImageException &e) {
		wxMessageDialog msg(main_panel_, _("No image to execute operation"));
		msg.ShowModal();
	}
}

void ImageEditorMainWindow::undo( wxCommandEvent& event ) {
	Editor *editor = Editor::getInstance();
	editor->unExecute();
}

void ImageEditorMainWindow::redo( wxCommandEvent& event ) {
	Editor *editor = Editor::getInstance();
	editor->redo();
}

void ImageEditorMainWindow::rotate_left(wxCommandEvent& event) {
	PCommand rotate_ccw(new RotateCcwCommand);
	doCommand(rotate_ccw);
}

void ImageEditorMainWindow::rotate_right(wxCommandEvent& event) {
	PCommand rotate_cw(new RotateCwCommand);
	doCommand(rotate_cw);
}

void ImageEditorMainWindow::flip_up_down( wxCommandEvent& event ) {
	PCommand flip_up_down(new FlipUpDownCommand);
	doCommand(flip_up_down);
}

void ImageEditorMainWindow::flip_left_right( wxCommandEvent& event ) {
	PCommand flip_left_right(new FlipLeftRightCommand);
	doCommand(flip_left_right);
}

void ImageEditorMainWindow::crop(wxCommandEvent& event) {
	Editor *editor = Editor::getInstance();
	try {
		ImageEditorCropWindow crop_window(main_panel_, editor->getImage()->getView().width() - 1,
						  editor->getImage()->getView().height() - 1);

		if(crop_window.ShowModal() == wxID_OK) {
			int x = crop_window.get_x();
			int y = crop_window.get_y();
			int width = crop_window.get_width();
			int height = crop_window.get_height();

			PCommand subimg(new SubimageCommand(x, y, width, height));
			doCommand(subimg);
		}
	}
	catch(Editor::NoImageException &e) {
		wxMessageDialog msg(main_panel_, _("No image to execute operation"));
		msg.ShowModal();
	}
}

void ImageEditorMainWindow::subsampled( wxCommandEvent& event ) {
	ImageEditorSubsmplWindow subsample_window(main_panel_);
	
	if(subsample_window.ShowModal() == wxID_OK) {
		int x_step = subsample_window.get_x_step();
		int y_step = subsample_window.get_y_step();

		PCommand subsmpl(new SubsampleCommand(x_step, y_step));
		doCommand(subsmpl);
	}
}

void ImageEditorMainWindow::close() {
//	int answer = wxMessageBox(_("Quit program?"), _("Confirm"), wxYES_NO | wxICON_QUESTION, main_panel);
//	if (answer == wxYES)
		exit(0);
}

void ImageEditorMainWindow::resize( wxSizeEvent& event ) {
	wxClientDC dc(main_panel_);
	if(!image_.IsOk())
		return;

	display(dc);
	main_panel_->Refresh();
}

void ImageEditorMainWindow::paint(wxPaintEvent& event) {
	wxPaintDC dc(main_panel_);
	if(!image_.IsOk())
		return;

	display(dc);
}

void ImageEditorMainWindow::display(wxDC& dc) {
	int new_w, new_h;
	dc.GetSize(&new_w, &new_h);

	if((new_w < image_.GetWidth() || new_h < image_.GetHeight()) && (new_w != panel_width_ || new_h != panel_height_)) {
		if(new_w < image_.GetWidth()) {
			new_h = image_.GetHeight() * new_w/image_.GetWidth();
		}
		if(new_h < image_.GetHeight()) {
			new_w = image_.GetWidth() * new_h/image_.GetHeight();
		}
		bitmap_ = wxBitmap(image_.Scale( new_w, new_h/*, wxIMAGE_QUALITY_HIGH */) );
    }

	panel_width_ = new_w;
	panel_height_ = new_h;
	x_middle_ = (panel_width_/2) - (bitmap_.GetWidth()/2);
	y_middle_ = (panel_height_/2) - (bitmap_.GetHeight()/2);

	dc.DrawBitmap(bitmap_, x_middle_, y_middle_);
}

void ImageEditorMainWindow::update() {
	Editor *editor = Editor::getInstance();
	Image::view_type image_view = editor->getImage()->getView();

	wxSize windowSize = main_panel_->GetSize();

	bool fit = true;
	if(windowSize.GetWidth() < image_view.width())
		fit = false;
	if(windowSize.GetHeight() < image_view.height())
		fit = false;

	int sample_factor = 1;
	Image::view_type resized_image_view = image_view;
	while(!fit) {
		++sample_factor;
		if(windowSize.GetWidth() >= resized_image_view.width()/sample_factor) {
			if(windowSize.GetHeight() >= resized_image_view.height()/sample_factor) {
				fit = true;
				resized_image_view = subsampled_view(resized_image_view, sample_factor, sample_factor);
			}
		}
	}

	image_ = wxImage((int) resized_image_view.width() , (int) resized_image_view.height());

	copy_pixels(resized_image_view, interleaved_view(image_.GetWidth(), image_.GetHeight(),
		(GILPixel *) image_.GetData(), image_.GetWidth()*3));

	bitmap_ = wxBitmap(image_);

	main_panel_->Refresh();

	wxClientDC dc(main_panel_);
	display(dc);
}
